package com.google.code.avajhelpdesk.components.formbuilder;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.ListMultipleChoice;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;

public class CustomBeanEditor extends Panel{

	private static final String LABEL_MARKUP_ID = "name";

	private static final String EDITOR_CONTAINER_MARKUP_ID = "editor";

	protected static interface Editor<T extends FormComponent<?>> {

		Component getContainerComponent();
		
		T getEditorComponent();
		
		IModel<?> getLabel();
	}
	
	private abstract class AbstractEditor<T extends FormComponent<?>> extends Fragment implements Editor<T> {

		private static final long serialVersionUID = 1L;

		private IModel<?> model;
		
		private IModel<?> label;

		private T editorComponent;
		
		public AbstractEditor(String id, String markupId, MarkupContainer markupProvider, 
				IModel<?> model, IModel<?> label) {

			super(id, markupId, markupProvider);

			this.model = model;
			this.label = label;
		}
		
		protected final void initialize() {
			editorComponent = createComponent();
			add(editorComponent);
		}
		
		protected abstract T createComponent();

		protected IModel<?> getEditorModel() {
			return model;
		}
		
		protected final String getEditorMarkupId() {
			return EDITOR_MARKUP_ID;
		}
		
		public Component getContainerComponent() {
			return this;
		}

		public IModel<?> getLabel() {
			return label;
		}

		public T getEditorComponent() {
			return editorComponent;
		}
	}
	
	private class TextFieldEditor extends AbstractEditor<TextField<?>> {
		
		private static final long serialVersionUID = 1L;
		
		private Class<?> type;
		
		public TextFieldEditor(String id, IModel<?> model, IModel<?> label, Class<?> type) {
			super(id, "textFieldEditor", CustomBeanEditor.this, model, label);
			
			initialize();
		}

		@Override
		@SuppressWarnings("unchecked")
		protected TextField<?> createComponent() {
			TextField component = new TextField(getEditorMarkupId(), getEditorModel(), type);
			component.setLabel(getLabel());
			
			return component;
		}
	}
	
	private class TextAreaEditor extends AbstractEditor<TextArea<?>> {

		private static final long serialVersionUID = 1L;
		
		public TextAreaEditor(String id, IModel<?> model, IModel<?> label) {
			super(id, "textAreaEditor", CustomBeanEditor.this, model, label);
		
			initialize();
		}

		@Override
		@SuppressWarnings("unchecked")
		protected TextArea<?> createComponent() {
			return new TextArea(getEditorMarkupId(), getEditorModel());
		}		
	}
	
	private class DropDownChoiceEditor extends AbstractEditor<DropDownChoice<?>> {
		
		private static final long serialVersionUID = 1L;

		private IModel<?> choices;
		
		private ChoiceRenderer<?> choiceRenderer;
		
		public DropDownChoiceEditor(String id, IModel<?> model, IModel<?> label, IModel<?> choices, ChoiceRenderer<?> choiceRenderer) {
			super(id, "dropDownChoiceEditor", CustomBeanEditor.this, model, label);
		
			this.choices = choices;
			this.choiceRenderer = choiceRenderer;
			
			initialize();
		}

		@Override
		@SuppressWarnings("unchecked")
		protected DropDownChoice<?> createComponent() {
			return new DropDownChoice(getEditorMarkupId(), getEditorModel(), choices);
		}
		
	}
	
	private class ListMultipleChoiceEditor extends AbstractEditor<ListMultipleChoice<?>> {

		private static final long serialVersionUID = 1L;

		private IModel<?> choices;
		
		private ChoiceRenderer<?> choiceRenderer;
		
		public ListMultipleChoiceEditor(String id, IModel<?> model, IModel<?> label, IModel<?> choices, ChoiceRenderer<?> choiceRenderer) {
			super(id, "listMultipleChoiceEditor", CustomBeanEditor.this, model, label);
		
			this.choices = choices;
			this.choiceRenderer = choiceRenderer;
			
			initialize();
		}
		
		@Override
		protected ListMultipleChoice<?> createComponent() {
			return new ListMultipleChoice(getEditorMarkupId(), getEditorModel(), choices, choiceRenderer);
		}
		
	}

	private static final long serialVersionUID = 1L;
	
	private static final String EDITOR_MARKUP_ID = "edit";

	private RepeatingView fields;
	
	public CustomBeanEditor(String id, IModel<?> toEdit, final FormSubmitListener listener) {
		super(id, toEdit);

		Form form = new Form("form") {
			protected void onSubmit() {
				listener.onSubmit(this, CustomBeanEditor.this
						.getDefaultModel());
			};
		};
		
		add(form);
		
	    fields = new RepeatingView("fields");
		form.add(fields);

		form.add(new FeedbackPanel("feedback"));
	}
	
	protected CustomBeanEditor append(Editor editor) {		
		WebMarkupContainer row = new WebMarkupContainer(fields.newChildId());
		fields.add(row);
		
		//add label for editor
		IModel<?> labelModel = editor.getLabel();
		row.add(new Label(LABEL_MARKUP_ID, labelModel));
		
		//add editor component
		row.add(editor.getContainerComponent());

		return this;
	}
	
	public TextField<?> appendTextField(String propertyName) {
		return appendTextField(propertyName, String.class);
	}	
	
	public TextField<?> appendTextField(String propertyName, Class<?> type) {
		TextFieldEditor stringEditor = 
			new TextFieldEditor(EDITOR_CONTAINER_MARKUP_ID, createModel(propertyName), createLabel(propertyName), type);
		
		append(stringEditor);
		
		return stringEditor.getEditorComponent();
	}
	
	public TextArea<?> appendTextArea(String propertyName) {
		TextAreaEditor editor = new TextAreaEditor(EDITOR_CONTAINER_MARKUP_ID, createModel(propertyName), createLabel(propertyName));
		append(editor);
		return editor.getEditorComponent();
	}

	public DropDownChoice<?> appendDropDownChoice(String propertyName, IModel<?> choices, ChoiceRenderer<?> choiceRenderer) {
		DropDownChoiceEditor editor = new DropDownChoiceEditor(EDITOR_CONTAINER_MARKUP_ID, createModel(propertyName), 
				createLabel(propertyName), choices, choiceRenderer);
		append(editor);
		return editor.getEditorComponent();
	}
	
	public ListMultipleChoice<?> appendListMultipleChoice(String propertyName, IModel<?> choices, ChoiceRenderer<?> choiceRenderer) {
		ListMultipleChoiceEditor editor = new ListMultipleChoiceEditor(EDITOR_CONTAINER_MARKUP_ID, createModel(propertyName), 
				createLabel(propertyName), choices, choiceRenderer);
		append(editor);
		return editor.getEditorComponent();
	}
	
	@SuppressWarnings("unchecked")
	private PropertyModel createModel(String propertyName) {
		return new PropertyModel(CustomBeanEditor.this.getDefaultModelObject(), propertyName);
	}

	private StringResourceModel createLabel(String propertyName) {
		return new StringResourceModel(propertyName, this, null);
	}
}